package handler;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

/**
 * Saves/Reads compressed,encrypted xml files from the filesystem.
 * @author Jared Schlicht
 */
public class CompressedEncryptedXMLDAO implements NotebookDAO {
    private XMLHandler xmlHandler;
    private CompressionHandler compressionHandler;
    private String filename;
    private EncryptionHandler encryptionHandler;
    private String password;
    
    /**
     * Constructs a new CompressedEncryptedXMLDAO object.
     * 
     * @author Jared Schlicht
     * @param filename the filename of the file to read or write
     * @param password The password to use to compress/decompress the file
     */
    public CompressedEncryptedXMLDAO(String filename, String password) {
        this.filename = filename;
        this.password = password;
        this.xmlHandler = new XMLHandler();
        this.compressionHandler = new CompressionHandler();
        this.encryptionHandler = new EncryptionHandler();
    }
    
    /**
     * Gets a list of equations from the encrypted,compressed file
     * @author Jared Schlicht
     */
    @Override
    public List<EquateNode> getEquations() {
        InputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(this.filename);
            // decrypt the file in RAM first
            ByteArrayOutputStream decryptedOutputStream = new ByteArrayOutputStream();
            this.encryptionHandler.decryptDES(this.password, fileInputStream, decryptedOutputStream);
            
            // decompress the data in RAM
            ByteArrayInputStream decryptedInputStream = new ByteArrayInputStream(decryptedOutputStream.toByteArray());
            ByteArrayOutputStream decompressedOutputStream = new ByteArrayOutputStream();
            
            this.compressionHandler.decompress(decryptedInputStream, decompressedOutputStream);
            
            // feed the decompressed data to the XML Handler
            ByteArrayInputStream xmlInputStream = new ByteArrayInputStream(decompressedOutputStream.toByteArray());
            
            return this.xmlHandler.getEquations(xmlInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();                    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }            
        }
    }

    /**
     * Writes the equations to an encrypted,compressed file.
     * @author Jared Schlicht
     */
    @Override
    public void saveEquations(List<EquateNode> notebookEquations) {
        OutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(this.filename);
            
            // convert the list to XML in memory first
            ByteArrayOutputStream xmlOutputStream = new ByteArrayOutputStream();
            this.xmlHandler.saveEquations(notebookEquations, xmlOutputStream);
            
            // compress the XML that's currently sitting in memory
            ByteArrayInputStream xmlInputStream = new ByteArrayInputStream(xmlOutputStream.toByteArray());
            ByteArrayOutputStream compressedOutputStream = new ByteArrayOutputStream();
            this.compressionHandler.compress(xmlInputStream, compressedOutputStream);
            
            ByteArrayInputStream compressedInputStream = new ByteArrayInputStream(compressedOutputStream.toByteArray());
            
            this.encryptionHandler.encryptDES(this.password, compressedInputStream, fileOutputStream);
            fileOutputStream.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();                    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }            
        }
    }
}
